home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magazyn WWW 1999 July
/
www_07_1999.iso
/
prog
/
mac
/
alpha
/
alpha.hqx
/
Alpha ƒ
/
Help
/
AppleEvents Help
< prev
next >
Wrap
Text File
|
1999-04-14
|
16KB
|
430 lines
##############################################(nowrap)#############
Alpha AppleEvent Packages
created: 4/9/98 {10:32:09 PM}
last update: 4/14/99 {2:10:26 PM}
version: 1.2
Author: Jonathan Guyer
E-mail: <jguyer@his.com>
www: <http://www.his.com/~jguyer/>
Copyright (c) 1998 Jonathan Guyer
###################################################################
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
###################################################################
Alpha's AEBuild command makes it (comparatively) easy to
interface with other applications, through the traumatic world of
AppleEvents (and Capture AE makes writing AE descriptors far easier
than it used to be, particulary for some inarticulate applications
(I won't mention any names, but a major offender rhymes with ╥Blinder╙)).
Using AEGizmos, one can build complicated event structures for
interaction with other programs.
Alpha automatically processes AppleEvent replies from those
programs with AEPrint, which renders them reasonably
human-readable, but quite unwieldy to deal with,
programmatically. Programmers wishing to work with AE from
within Alpha have been forced to write clumsy regexpen to attempt
to extract the elements of interest from event replies, but
because of the potential complexity of AE reply records, these
regexpen almost invariably fail to account for all contingencies.
To make it easier to work with AppleEvent replies (and, by
extension, to process events for which Alpha has been declared a
handler), I have written a suite of packages: aeparse.tcl,
aecoerce.tcl, and aebuild.tcl. The first two packages work in concert
to implement a reasonable facsimile of Jens Alfke's
╥formidable-looking BNF grammar specification╙, parsing AEPrint
strings into Tcl-readable lists of lists. The third package
essentially replaces the routines in appleEvents.tcl with a more
consistently named set of routines. Stub routines remain in
appleEvents.tcl to map to the new format.
Given an ╥aevt\ansr{...}╙, a ╥GURL\GURL{...}╙, or some subset of an
AEGizmo string, you can parse to a list of lists; keyword searches
are then easy.
If you call:
aeparse::event [AEBuild bladdity blah]
you get back a typed and tagged list of lists. If the reply
from [AEBuild bladdity blah] is:
aevt\ansr{BLAH:5, blah:╥Blah?╙}
then aeparse::event will return
reco {{BLAH {long 5}} {blah {TEXT Blah?}}}
All the extra tags may seem cumbersome, but the default behavior of
the routines is to strip them out. For instance:
aeparse::keywordValue blah [aeparse::event [AEBuild bladdity blah]]
will just return:
Blah?
If you don't know in advance that the 'blah' keyword holds a string,
then aeparse::keywordValue takes an optional typed flag, so you'll get back
TEXT Blah?
Since, for many event replies, the direct object is what's wanted,
there is a short-hand to replace
aeparse::keywordValue ---- [aeparse::event [AEBuild bladdity blah]]
with
aebuild::result bladdity blah
In the event of AppleEvent errors, aeparse::event automatically checks
for 'errn' and 'errs' tags and throws an appropriate error. The
array 'aeparse::errors' holds error messages to be used when no
'errs' tag accompanies 'errn' (the Finder does this); see the
initialization code for aeparse for examples of the format.
In addition to parsing AE descriptor strings, calls to
aeparse::event automatically perform any type coercions that have
been declared with aecoerce::register. For example:
aeparse::event {aevt\ansr{----:bool(╟01╚)}}
will not return
reco {{---- {bool {hexd 01}}}}
but rather
reco {{---- {bool 1}}}
so that
aeparse::keywordValue "----" \
[aeparse::event {aevt\ansr{----:bool(╟01╚)}}]
just returns ╥1╙; no further processing is necessary.
A more powerful example is seen in a rewrite of
mailboxPathIndex in eudoraMenu.tcl:
proc mailboxPathIndex {ind} {
set res [objectProperty 'CSOm' euFS [mailboxByIndex $ind]]
return [extractPath $res]
}
'res' will hold something like
aevt\ansr{'----':fss (╟FFFD000001CC02496E00002F52FE002F52827FFF7FFF7FFF7F0001C70FC00000F90001D3F9547FFF7FFF01C95F5001D33C5E01D33BFC004120B401D33C4401D3CCCCCCCCCCCC╚)}
extractPath looks for anything between ╟ and ╚ and attempts to
coerce that from an 'fss ' to a path, returning
documents:Eudora:HIS:In
This example happens to work, but some gross assumptions have
been made about the format of the returned AE descriptor string
and explicit coercions are needed after every AEBuild call. Using
the routines of this package, we can instead write:
proc mailboxPathIndex {ind} {
set res [aeparse::event [objectProperty 'CSOm' euFS [mailboxByIndex $ind]]]
return [aeparse::keywordValue "----" $res]
}
In this case, 'res' holds
reco {{---- {{fss } documents:Eudora:HIS:In}}}
Although the code is not any shorter, the coercion of 'fss ' has
been performed automatically (and not arbitrarily). Although
this list of lists looks like a mess, it is easily processed by
aeparse::keywordValue, so you need not worry about its
internals. We can simplify 'mailboxPathIndex' even further by
writing:
proc mailboxPathIndex {ind} {
return [aebuild::result 'CSOm' core getd ---- \
[propertyObject euFS [mailboxByIndex $ind]]]
}
or, better yet,
proc mailboxPathIndex {ind} {
return [aebuild::objectProperty 'CSOm' euFS [mailboxByIndex $ind]]
}
CAVEAT: Because the AE descriptor string is being completely
parsed, aeparse::event can be rather slower than blind regexping.
The trade-off is that the results you obtain are much less
likely to be erroneous, especially for error conditions. I will be
looking for ways to speed things up, though.
_________________________________________________________________
Cheater's Guide to AppleEvent Descriptors
_________________________________________________________________
Until I found out about Capture AE (thanks Tom!), there was no simple
way to figure out how to write an AEDesc for some applications (I've
never learned anything useful from AETracker, but YMMV). Ironically,
the Finder is one of the hardest, at least in part because its
scripting model was developed concurrently with, and in some cases
before, the OSA architecture as a whole was developed. Determining
something as trivial as a list of the available disks which, in
AppleScript, is
tell application "Finder" to get disks
can be an unholy nightmare to translate into AEGizmos. Fortunately,
if you activate Capture AE, execution of this script dumps
Process("Finder").SendAE "core,getd,'----':obj {form:indx, want:type(cdis), seld:abso(╟616C6C20╚), from:'null'()}"
The '&inte' and '&timo' keys are not relevant to us, so this is
easily enough rendered in Tcl as
AEBuild -r 'MACS' core getd ---- {obj {form:indx, want:type(cdis), seld:abso(╟616C6C20╚), from:'null'()}}
Although it works, "abso(╟616C6C20╚)" isn't very nice to look at. A
quick pass with aecoerce::hexd:TEXT (or perusal of an ASCII chart)
reveals this is, reasonably enough, the identifier 'all ', so we
substitute that and make use of this suite of routines, yielding
aebuild::result 'MACS' core getd ---- {obj {form:indx, want:type(cdis), seld:abso(all), from:'null'()}}
which... unfortunately... returns
{reco {{want {type prop}} {from {null {}}} {form {type prop}} {seld {type sdsk}}}} {reco {{want {type cdis}} {from {null {}}} {form {type name}} {seld {TEXT catbert}}}} {reco {{want {type cdis}} {from {null {}}} {form {type name}} {seld {TEXT ratbert}}}}}
which is a parsing nightmare (note that, on top of everything else, the
Startup Disk is treated differently from everything else). Fortunately,
we can make the Finder do our work for us by using the 'rtyp' tag
aebuild::result 'MACS' core getd rtyp TEXT ---- {obj {form:indx, want:type(cdis), seld:abso('all '), from:'null'()}}
which yields a manageable
catbert: dogbert: ratbert:
on my computer.
Even with Capture AE, it's handy to be able to read 'aete' resources
(if for no other reason, to decipher what you just blindly copied
into your AEBuild statements). For this, Yuji Hachiya's 'aete' editor
for ResEdit is indispensible.
If, for some reason, Capture AE is unavailable to you (redistribution
is prohibited, so WestCodeSoft could conceivably withdraw it), there
is, fortunately, a trick you can try:
1) Enter the above AppleScript in the Script Editor and save it as
a compiled script.
2) Open the script in a resource editor, open the 'scpt' resources,
and open what will likely be the only 'scpt' resource there
(if you have a 'scpt' resource editor installed, you'll want to
be sure to open the resource as hexadecimal).
3) Look for the first occurence of the string 'MACS' (╟4D414353╚)
and change this to 'ALFA' (╟414C4641╚). Ignore any explicit
references to "Finder"; they don't matter. You've just changed the
script to ask Alpha for a list of all disks. Alpha obviously doesn't
know how to do this, but that's not the point.
4) Close and save your changes.
5) Return to Alpha and write a dummy Tcl routine to intercept this event,
such as
proc snoop {event} {}
It needn't do any more than this. aeparse::event is decidedly not
your friend for this task (try it if you don't believe me).
6) Declare 'snoop' as an AppleEvent handler for the event you wish to
examine:
eventHandler core getd snoop
7) Place a trace on 'snoop'.
8) Run your modified AppleScript applette from the Finder.
9) Return to Alpha and dump traces.
For the case above we obtain
snoop 'core\getd{'----':obj {form:indx, want:type(cdis), seld:abso(╟616C6C20╚), from:'null'()}, &inte:cans, &timo:3600}'
snoop OK:
Although this probably seems like a lot of work, I assure you that
it's far preferable to the hunt-and-peck alternative. There's no
reason that this methodology won't work with other scriptable
applications, either, but I take no responsibility for the implosion
of your monitor.
_________________________________________________________________
Lexicon
_________________________________________________________________
There are a plethora of routines in these packages, and the
majority of the parsing routines, in particular, will probably
never be called directly. The ones most programmers will ever
have need to call are listed below.
Note: If you do have reason to parse some sub-string of an
AEDesc, keep in mind that, with the exception of aeparse::event,
all of the token and grammar parsers operate in place, i.e., they
are not passed a string, but the name of a string and, when
finished, the parsed material is removed from the start of the
string.
_________________________________________________________________
AEParse
_________________________________________________________________
NAME
aeparse::event - Parse a textual event record, as returned by AEPrint.
SYNOPSIS
aeparse::event chars ?-all? ?-coerce coercions?
?-noCoerce noCoercions?
_________________________________________________________________
DESCRIPTION
Optional parameters:
-all: Return the entire parsed event structure, not just the AE
descriptor record. The default behavior omits the class and the
event. The reply from a call to AEBuild is always(?) an
╥aevt\ansr╙, but Alpha event handlers could potentially receive
anything and may have use for this information.
-coerce: Temporarily override (or supply) the specified type
coercions. Takes a list of lists, each element of which is
{'fromType' 'toType' 'coercionProc'}.
-noCoerce: Do not perform the specified type coercions. This is
useful, for instance, to prevent the automatic coercion of
'alis's or 'fss 's to paths; this coercion is time consuming and
may not always be of interest. Takes a list of lists, each
element of which is {'fromType' 'toType'}. glob style
wildcards can be used to block a whole family of coercions,
e.g., ╥-noCoerce {{hexd *}}╙ will prevent all hexd coercions.
_________________________________________________________________
NAME
aeparse::keywordValue - Return value of keyword from AE record.
SYNOPSIS
aeparse::keywordValue keyword record ?typed?
_________________________________________________________________
DESCRIPTION
Return the value of the specified keyword from a parsed
AppleEvent record.
Optional parameter:
typed: Boolean flag determines whether the value is returned as a
list {type value} or simply as value. Default is false.
_________________________________________________________________
AECoerce
_________________________________________________________________
NAME
aecoerce::apply - coerce an AE value.
SYNOPSIS
aecoerce::apply value type
_________________________________________________________________
DESCRIPTION
'value' is coerced to 'type'. 'value' must be a list of
{'oldType' 'value'}. If a coercion from 'oldType' to 'type' has
not been declared with aecoerce::register, or with the
optional -coerce parameter to aeparse::event, then an error is
thrown.
_________________________________________________________________
NAME
aecoerce::register - register a coercion procedure.
SYNOPSIS
aecoerce::register from to proc
_________________________________________________________________
DESCRIPTION
Registers 'proc' to handle coercions from AE type 'from' to AE
type 'to'. Proc must take a single argument of type 'from' and
return a value of type 'to'. E.g.,
aecoerce::register "hexd" "fss " specToPathName
will handle the coercion of ╥fss (╟FFFD000001C╔CCCCC╚)╙ to
{{fss } {documents:Eudora:HIS:In}}.
_________________________________________________________________
AEBuild
_________________________________________________________________
NAME
aebuild::result - Build an AppleEvent and return the parsed
direct object of its result.
SYNOPSIS
aebuild::result args
_________________________________________________________________
DESCRIPTION
The arguments to this command are those of AEBuild.
AEBuild's ╘-r╒ option to is not required, but is not harmful,
either. The ╘-q╒ option is not meaningful in this context,
and an ╥Unexpected end of format string╙ error will be thrown.
There is no mechanism for overriding coercions with this call. If
you wish to do that, you will need to use the long-hand
aeparse::keywordValue ---- [aeparse::event [AEBuild -r ...] -coerce ...]
_________________________________________________________________
NAME
aebuild::fromParsed - Turn list of lists (generated by aeparse
routines) into AEGizmo strings.
SYNOPSIS
aebuild::fromParsed args
_________________________________________________________________
DESCRIPTION
The arguments to this command are those returned by aeparse::event
(or a subset thereof).
aebuild::fromParsed [aeparse::event blah]
should be commutative (but there are surely many cases where this
fails for now).
_________________________________________________________________
NAME
aebuild::objectProperty - Return specified property.
SYNOPSIS
aebuild::objectProperty process property object
_________________________________________________________________
DESCRIPTION
Ask 'process' for 'property' of 'object'. There is no mechanism for
overriding coercions with this call.